﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using EnumClassLibrary;
using Model;

namespace WindowsTest
{
    /// <summary>
    /// 影像分析
    /// </summary>
    public class ImageAnalysis
    {
        /// <summary>
        /// 找左边竖线
        /// </summary>
        /// <param name="bm">输入的采样后影像 </param>
        /// <param name="im">影像的二值矩阵 </param>
        /// <param name="x">边界像素占竖行的百分比</param>
        public static Bitmap FindLeftLines(Bitmap bm, byte[,] im, float x)
        {
            var w = bm.Width;
            var h = bm.Height;
            var gs = new int[w, h];
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int i = 1; i < w - 1; i++)
                {
                    for (int j = 1; j < h - 1; j++)
                    {
                        if (im[i - 1, j] == 0 && im[i + 1, j] == 1)
                        {
                            gs[i, j] = 1;
                        }
                    }
                }

                for (int i = 0; i < w; i++)
                {
                    var s = 0;
                    for (int j = 0; j < h; j++)
                    {
                        s += gs[i, j];
                    }

                    if ((float)s / h > x)
                    {
                        var startX = 0;
                        var endX = 0;
                        for (int j = 0; j < h; j++)
                        {
                            if (gs[i, j] == 1)
                            {
                                startX = j;
                                break;
                            }
                        }

                        for (int j = h - 1; j > 0; j--)
                        {
                            if (gs[i, j] == 1)
                            {
                                endX = j;
                                break;
                            }
                        }

                        for (int j = startX; j < endX; j++)
                        {
                            (p + data.Stride * j + i * 3)[0] = 0;
                            (p + data.Stride * j + i * 3)[1] = 0;
                            (p + data.Stride * j + i * 3)[2] = 255;
                        }
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 找右边竖线
        /// </summary>
        /// <param name="bm">输入的采样后影像 </param>
        /// <param name="im">影像的二值矩阵 </param>
        /// <param name="x">边界像素占竖行的百分比</param>
        public static Bitmap FindRightLines(Bitmap bm, byte[,] im, float x)
        {
            var w = bm.Width;
            var h = bm.Height;
            var gs = new int[w, h];
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int i = 1; i < w - 1; i++)
                {
                    for (int j = 1; j < h - 1; j++)
                    {
                        if (im[i + 1, j] == 0 && im[i - 1, j] == 1)
                        {
                            gs[i, j] = 1;
                        }
                    }
                }

                for (int i = 0; i < w; i++)
                {
                    var s = 0;
                    for (int j = 0; j < h; j++)
                    {
                        s += gs[i, j];
                    }

                    if ((float)s / h > x)
                    {
                        var startX = 0;
                        var endX = 0;
                        for (int j = 0; j < h; j++)
                        {
                            if (gs[i, j] == 1)
                            {
                                startX = j;
                                break;
                            }
                        }

                        for (int j = h - 1; j > 0; j--)
                        {
                            if (gs[i, j] == 1)
                            {
                                endX = j;
                                break;
                            }
                        }

                        for (int j = startX; j < endX; j++)
                        {
                            (p + data.Stride * j + i * 3)[0] = 0;
                            (p + data.Stride * j + i * 3)[1] = 0;
                            (p + data.Stride * j + i * 3)[2] = 255;
                        }
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 将横向空白区画横线
        /// </summary>
        /// <param name="bm">输入的采样后影像 </param>
        /// <param name="im">影像的二值矩阵 </param>
        /// <param name="x">每行（像素行）允许的噪点百分比（如：0.1f）</param>
        public static Bitmap FindTransverseSpace(Bitmap bm, byte[,] im, float x)
        {
            var w = bm.Width;
            var h = bm.Height;
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int j = 0; j < h; j++)
                {
                    var s = 0;
                    var ptr = p + data.Stride * j;
                    for (int i = 0; i < w; i++)
                    {
                        s = s + im[i, j];
                    }
                    if ((float)s / w < x)
                    {
                        for (int i = 0; i < w; i++)
                        {
                            ptr[0] = 0;
                            ptr[1] = 0;
                            ptr[2] = 255;
                            ptr += 3;
                        }
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 红色区域涂白
        /// </summary>
        public static Bitmap Getfield(Bitmap bm)
        {
            var w = bm.Width;
            var h = bm.Height;
            var gs = new int[w, h];
            BitmapData data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                                           PixelFormat.Format24bppRgb);

            unsafe
            {
                var p = (byte*)data.Scan0.ToPointer();
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        if ((p + j * data.Stride + i * 3)[0] == 0 && (p + j * data.Stride + i * 3)[1] == 0 && (p + j * data.Stride + i * 3)[2] == 255)
                        {
                            gs[i, j] = 1;
                        }
                    }
                }

                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        (p + j * data.Stride + i * 3)[0] =
                                (p + j * data.Stride + i * 3)[1] = (p + j * data.Stride + i * 3)[2] = gs[i, j] == 0 ? (byte)0 : (byte)255;
                    }
                }
            }

            bm.UnlockBits(data);

            return bm;
        }

        /// <summary>
        /// 找黑色矩形快
        /// </summary>
        /// <param name="bm">输入的影像 </param>
        /// <returns>输出的目标图像</returns>
        public static List<Rectangle> FindRectangle(Bitmap bm)
        {
            var w = bm.Width;
            var h = bm.Height;
            var rectangles = new List<Rectangle>();
            var im = Common.ToBinary(bm, Matlab.GetThreshold(bm));

            for (int j = 1; j < h - 1; j++)
            {
                for (int i = 1; i < w - 1; i++)
                {
                    if (im[i, j] == 0) { continue; }
                    if (im[i, j - 1] == 1) { continue; }
                    if (im[i + 1, j - 1] == 1) { continue; }
                    if (im[i + 1, j] == 0) { continue; }
                    if (im[i + 1, j + 1] == 0) { continue; }
                    if (im[i, j + 1] == 0) { continue; }
                    if (im[i - 1, j + 1] == 1) { continue; }
                    if (im[i - 1, j] == 1) { continue; }
                    if (im[i - 1, j - 1] == 1) { continue; }

                    var x = i;
                    var flagX = true;
                    do
                    {
                        if (x == w) break;
                        if (im[x, j] == 0)
                        {
                            flagX = false;
                        }
                        x++;
                    } while (flagX);

                    var y = j;
                    var flagY = true;
                    do
                    {
                        if (y == h) break;
                        if (im[i, y] == 0)
                        {
                            flagY = false;
                        }
                        y++;
                    } while (flagY);

                    if (y - j < 16) { continue; }
                    if (x - i < w / 8) { continue; }
                    if (x == w) { continue; }
                    if (y == h) { continue; }

                    rectangles.Add(new Rectangle(i, j, x - i, y - j));
                }
            }

            return rectangles;
        }

        /// <summary>
        /// 保存分割图片
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <param name="rs">矩形内容区集合</param>
        /// <param name="path">保存的路径（以“\”结束）</param>
        public static void SaveImageToTif(Bitmap bm, List<Rectangle> rs, string path)
        {
            var w = bm.Width;
            var h = bm.Height;
            var data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            unsafe
            {
                var ptr = (byte*)data.Scan0.ToPointer();
                var n = 0;
                foreach (var rectangle in rs)
                {
                    n++;
                    var obm = new Bitmap(rectangle.Width, rectangle.Height);
                    var obmdata = obm.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height),
                                               ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                    var p = (byte*)obmdata.Scan0.ToPointer();
                    for (int j = rectangle.Y; j < rectangle.Height + rectangle.Y; j++)
                    {
                        for (int i = rectangle.X; i < rectangle.Width + rectangle.X; i++)
                        {
                            var _p = p + (j - rectangle.Y) * obmdata.Stride + (i - rectangle.X) * 3;
                            var _ptr = ptr + j * data.Stride + i * 3;
                            _p[0] = _ptr[0];
                            _p[1] = _ptr[1];
                            _p[2] = _ptr[2];
                        }
                    }

                    obm.UnlockBits(obmdata);
                    obm = Matlab.RectangleEageEnlarge(obm, 20, 20);
                    obm.Save(@path + n + ".tif", ImageFormat.Tiff);
                }
            }
            bm.UnlockBits(data);
        }

        /// <summary>
        /// 保存TIF图片
        /// </summary>
        /// <param name="bm">二值化影像</param>
        /// <param name="rs">包含药品的矩形框列表</param>
        /// <param name="path">要保存的路径</param>
        public static void SaveImageToTifForListImage(Bitmap bm, List<Rectangle> rs, string path)
        {
            var w = bm.Width;
            var h = bm.Height;
            var data = bm.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            unsafe
            {
                var ptr = (byte*)data.Scan0.ToPointer();
                var n = 0;
                foreach (var rectangle in rs)
                {
                    n++;
                    var rw = rectangle.Width;
                    var rh = rectangle.Height;

                    var _jMin = rectangle.Y - 10 < 0 ? rectangle.Y : rectangle.Y - 10;
                    var _iMin = rectangle.X - 10 < 0 ? rectangle.X : rectangle.X - 10;
                    var _jMax = rh + rectangle.Y + 10 > h ? rh + rectangle.Y : rh + rectangle.Y + 10;
                    var _iMax = rw + rectangle.X + 10 > w ? rw + rectangle.X : rw + rectangle.X + 10;

                    var obm = new Bitmap(_iMax - _iMin, _jMax - _jMin);
                    var obmdata = obm.LockBits(new Rectangle(0, 0, _iMax - _iMin, _jMax - _jMin),
                                               ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                    var p = (byte*)obmdata.Scan0.ToPointer();
                    for (int j = _jMin; j < _jMax; j++)
                    {
                        for (int i = _iMin; i < _iMax; i++)
                        {
                            var _p = p + (j - _jMin) * rw * 3 + (i - _iMin) * 3 +
                                     (obmdata.Stride - rw * 3) * (j - _jMin);
                            _p[0] = (ptr + j * data.Stride + i * 3)[0];
                            _p[1] = (ptr + j * data.Stride + i * 3)[1];
                            _p[2] = (ptr + j * data.Stride + i * 3)[2];
                        }
                    }

                    obm.UnlockBits(obmdata);
                    obm.Save(@path + n + ".tif", ImageFormat.Tiff);
                }
            }
            bm.UnlockBits(data);
        }

        /// <summary>
        /// 找黑色矩形快
        /// </summary>
        /// <returns>输出的目标图像</returns>
        public static List<Rectangle> FindRectangle(List<Line> lines)
        {
            var rectangles = new List<Rectangle>();

            return rectangles;
        }

        /// <summary>
        /// 矩形渐变
        /// </summary>
        /// <param name="bm">输入的影像</param>
        /// <param name="im">输入的影像二值矩阵</param>
        /// <param name="space"> </param>
        /// <returns>矩形渐变后的影像</returns>
        public static byte[,] RectangleGradient(Bitmap bm, byte[,] im, int space)
        {
            var w = bm.Width;
            var h = bm.Height;

            for (int j = 0; j < h; j++)
            {
                for (int i = 0; i < w; i++)
                {
                    if (im[i, j] == 0)
                    {
                        continue;
                    }

                    for (int k = space; k > 0; k--)
                    {
                        if (im[i + k, j] == 1)
                        {

                        }
                    }
                }
            }


            return im;
        }

        /// <summary>
        /// 二值采样
        /// </summary>
        /// <param name="im">输入的影像二值矩阵</param>
        /// <param name="x">采样间隔 </param>
        /// <returns>32像素二值采样后的影像</returns>
        public static byte[,] BitmapSampling(byte[,] im, int x)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var flag = true;
            for (int j = x; j < h; j++)
            {
                for (int i = x; i < w; i++)
                {
                    var s = 0;
                    for (int k = 0; k < x; k++)
                    {
                        for (int l = 0; l < x; l++)
                        {
                            var z = i + l >= w ? w - 1 : i + l;
                            var y = j + k >= h ? h - 1 : j + k;
                            s += im[z, y];
                        }
                    }

                    if (flag)
                    {
                        if (s > 10)
                        {
                            for (int k = 0; k < x; k++)
                            {
                                for (int l = 0; l < x; l++)
                                {
                                    im[i + l >= w ? w - 1 : i + l, j + k >= h ? h - 1 : j + k] = 1;
                                }
                            }
                            flag = false;
                        }
                    }
                    else
                    {
                        if (s > (x - 1) * x)
                        {
                            for (int k = 0; k < x; k++)
                            {
                                for (int l = 0; l < x; l++)
                                {
                                    im[i + l >= w ? w - 1 : i + l, j + k >= h ? h - 1 : j + k] = 1;
                                }
                            }
                        }
                        else
                        {
                            flag = true;
                        }
                    }
                }
            }

            return im;
        }

        #region 找线

        /// <summary>
        /// 得到模版直线
        /// </summary>
        /// <param name="bm">影像</param>
        /// <param name="tinnerHLines">最终横线</param>
        /// <param name="tinnerVLines">最终竖线</param>
        public static Bitmap GetModelLines(Bitmap bm, out List<Line> tinnerHLines, out List<Line> tinnerVLines)
        {
            var iw = bm.Width;
            var ih = bm.Height;
            var newBitmap = ImageCorrect.ImageRectification(bm);//纠偏
            var im = Common.ToBinary(newBitmap, Matlab.GetThreshold(newBitmap));
            var hLines = FindHorizontalLines(im, 8, iw / 5, iw);//找横线
            var vLines = FindVerticalLines(im, 8, ih / 8, ih);//找竖线

            //将所有线细化
            tinnerHLines = GetTinnerLines(hLines);
            tinnerVLines = GetTinnerLines(vLines);

            //去噪音线
            tinnerHLines = DeleteNoiseLines(tinnerHLines, tinnerVLines, 28, iw / 4);
            tinnerVLines = DeleteNoiseLines(tinnerVLines, tinnerHLines, 50, ih / 4);

            //去边界线
            DeleteEdgeLines(ref tinnerHLines, ref tinnerVLines, iw, ih);
            //直线补全
            //LinesComplete(ref tinnerHLines, ref tinnerVLines, iw, ih, 100);
            //直线修正
            CorrectLines(ref tinnerHLines, ref tinnerVLines, 50);

            return newBitmap;
        }

        /// <summary>
        /// 找横线
        /// </summary>
        /// <param name="im">影像的二值矩阵</param>
        /// <param name="space">允许断开的距离</param>
        /// <param name="minLength">直线最小长度</param>
        /// <param name="maxLength">直线最大长度</param>
        /// <returns>横线集合</returns>
        public static List<Line> FindHorizontalLines(byte[,] im, int space, int minLength, int maxLength)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var lines = new List<Line>();

            for (int j = 1; j < h - 1; j++)
            {
                for (int i = 1; i < w - space; i++)
                {
                    if (im[i, j] == 1)
                    {
                        var line = new Line { A = j, B_Min = i, B_Max = i };
                        var flag = true;
                        var y = j;
                        while (flag)
                        {
                            i++;
                            if (i > w - space - 1) break;
                            flag = false;
                            if (im[i, y] == 1)
                            {
                                flag = true;
                                line.B_Max = i;
                            }
                            else
                            {
                                if (y - 1 > 1 && im[i, y - 1] == 1)
                                {
                                    flag = true;
                                    y = y - 1;
                                    line.B_Max = i;
                                    continue;
                                }

                                if (y + 1 < h - 1 && im[i, y + 1] == 1)
                                {
                                    flag = true;
                                    y = y + 1;
                                    line.B_Max = i;
                                    continue;
                                }

                                for (int k = space; k > 0; k--)
                                {
                                    if (im[i + k, y] == 1)
                                    {
                                        flag = true;
                                        line.B_Max = i + k;
                                        i = i + k;
                                        break;
                                    }

                                    if (y - 1 > 1 && im[i + k, y - 1] == 1)
                                    {
                                        flag = true;
                                        y = y - 1;
                                        line.B_Max = i + k;
                                        i = i + k;
                                        break;
                                    }

                                    if (y + 1 < h - 1 && im[i + k, y + 1] == 1)
                                    {
                                        flag = true;
                                        y = y + 1;
                                        line.B_Max = i + k;
                                        i = i + k;
                                        break;
                                    }
                                }
                            }
                        }

                        var length = line.B_Max - line.B_Min;
                        if (length > minLength && length < maxLength)
                        {
                            lines.Add(line);
                        }
                    }
                }
            }

            return lines;
        }

        /// <summary>
        /// 找竖线
        /// </summary>
        /// <param name="im">影像的二值矩阵</param>
        /// <param name="space">允许断开的距离</param>
        /// <param name="minLength">直线最小长度</param>
        /// <param name="maxLength">直线最大长度</param>
        /// <returns>竖线集合</returns>
        public static List<Line> FindVerticalLines(byte[,] im, int space, int minLength, int maxLength)
        {
            var w = im.GetLength(0);
            var h = im.GetLength(1);
            var lines = new List<Line>();

            for (int i = 1; i < w - 1; i++)
            {
                for (int j = 1; j < h - space; j++)
                {
                    if (im[i, j] == 1)
                    {
                        var line = new Line { A = i, B_Min = j, B_Max = j };
                        var flag = true;
                        var x = i;
                        while (flag)
                        {
                            j++;
                            if (j > h - space - 1) break;
                            flag = false;
                            if (im[x, j] == 1)
                            {
                                flag = true;
                                line.B_Max = j;
                            }
                            else
                            {
                                if (x - 1 > 1 && im[x - 1, j] == 1)
                                {
                                    flag = true;
                                    x = x - 1;
                                    line.B_Max = j;
                                    continue;
                                }

                                if (x + 1 < w - 1 && im[x + 1, j] == 1)
                                {
                                    flag = true;
                                    x = x + 1;
                                    line.B_Max = j;
                                    continue;
                                }

                                for (int k = space; k > 0; k--)
                                {
                                    if (im[i, j + k] == 1)
                                    {
                                        flag = true;
                                        line.B_Max = j + k;
                                        j = j + k;
                                        break;
                                    }

                                    if (x - 1 > 1 && im[x - 1, j + k] == 1)
                                    {
                                        flag = true;
                                        x = x - 1;
                                        line.B_Max = j + k;
                                        j = j + k;
                                        break;
                                    }

                                    if (x + 1 < w - 1 && im[x + 1, j + k] == 1)
                                    {
                                        flag = true;
                                        x = x + 1;
                                        line.B_Max = j + k;
                                        j = j + k;
                                        break;
                                    }
                                }
                            }
                        }

                        var length = line.B_Max - line.B_Min;
                        if (length > minLength && length < maxLength)
                        {
                            lines.Add(line);
                        }
                    }
                }
            }

            return lines;
        }

        #region 直线细化方法

        /// <summary>
        /// 直线细化算法，找边界线，删除边界线，处理直线的长度（取最长的直线）
        /// </summary>
        /// <param name="lines">所有直线集合</param>
        /// <returns>细化后的直线集合</returns>
        public static List<Line> GetTinnerLines(List<Line> lines)
        {
            var tLines = new List<Line>();

            int i;
            for (i = 0; i < lines.Count; )
            {
                var tempLine = lines[i];
                tempLine = getTempLine(lines, tempLine);
                var n = 0;
                var line = getTinnerLine(ref lines, tempLine, ref n);
                line.A = line.A + n / 2;
                tLines.Add(line);
            }

            return tLines;
        }

        /// <summary>
        /// 获取模板直线，模版直线的长度等于直线簇的范围长度
        /// </summary>
        /// <param name="lines">所有直线集合</param>
        /// <param name="tempLine">模板基线</param>
        /// <returns>模板直线</returns>
        private static Line getTempLine(List<Line> lines, Line tempLine)
        {
            var afterLines = lines.FindAll(l => l.A == tempLine.A + 1);

            if (afterLines.Count > 0)
            {
                var flag = false;
                foreach (Line line in afterLines)
                {
                    if (IsOnSameCluster(tempLine, line))
                    {
                        flag = true;
                        tempLine = setLinesLengthAsBigger(tempLine, line, line.A);
                    }
                }

                if (flag)
                {
                    tempLine = getTempLine(lines, tempLine);
                }
            }

            return tempLine;
        }

        /// <summary>
        /// 根据模版直线细化直线簇，得到细化后的直线
        /// </summary>
        /// <param name="lines">所有直线集合</param>
        /// <param name="n">模版直线位移量，用于找中线，将细化后的直线定位到细化前直线簇的中间位置</param>
        /// <param name="tempLine">模板基线</param>
        /// <returns>细化后的直线</returns>
        private static Line getTinnerLine(ref List<Line> lines, Line tempLine, ref int n)
        {
            //反向查找，保证不漏查
            var beforeLines = lines.FindAll(l => l.A == tempLine.A);

            if (beforeLines.Count > 0)
            {
                //是否执行递归标志，当找到的直线中有与模板线同簇时，继续执行递归，否则认为当前直线簇已找完
                var flag = false;

                //相邻直线可能有多条，所以循环
                foreach (Line line in beforeLines)
                {
                    if (IsOnSameCluster(tempLine, line))
                    {
                        flag = true;
                        tempLine = setLinesLengthAsBigger(tempLine, line, line.A);
                        //满足条件的直线都从直线集合中移除，方便找细化下个直线簇
                        lines.Remove(line);
                    }
                }

                if (flag)
                {
                    //找到相邻直线后，模板直线相应左移
                    tempLine.A = tempLine.A - 1;
                    n++;
                    //递归，找下一条或多条相邻直线
                    tempLine = getTinnerLine(ref lines, tempLine, ref n);
                }
            }

            return tempLine;
        }

        /// <summary>
        /// 设置直线长度，取两直线的范围长
        /// </summary>
        /// <param name="line1">第一条直线</param>
        /// <param name="line2">第二条直线</param>
        /// <param name="lineA">同轴坐标</param>
        /// <returns>设置长度后的直线</returns>
        private static Line setLinesLengthAsBigger(Line line1, Line line2, int lineA)
        {
            var line = new Line
                           {
                               A = lineA,
                               B_Min = line1.B_Min < line2.B_Min ? line1.B_Min : line2.B_Min,
                               B_Max = line1.B_Max > line2.B_Max ? line1.B_Max : line2.B_Max
                           };

            return line;
        }

        #endregion

        #region 去噪音线方法

        /// <summary>
        /// 根据阈值(两线之间的最小距离)删除噪音线
        /// </summary>
        /// <param name="lines">要除噪音线的集合</param>
        /// <param name="verticalLines">与除噪音线集合垂直的线集合</param>
        /// <param name="x">阈值（两条线相隔最小距离）</param>
        /// <param name="minLength">孤立线最小长度</param>
        /// <returns>去除噪音线后的直线集合</returns>
        public static List<Line> DeleteNoiseLines(List<Line> lines, List<Line> verticalLines, int x, int minLength)
        {
            for (int i = 0; i < lines.Count; i++)
            {
                //判断是否是孤立线
                if (isIsolateLine(lines[i], verticalLines, minLength))
                {
                    lines.Remove(lines[i]);
                    i--;
                    continue;
                }
                if (removeNoiseLine(ref lines, lines[i], verticalLines, x))
                {
                    i--;
                }
            }

            return lines;
        }

        /// <summary>
        /// 删除阈值内两条直线中较短的直线，并返回是否成功移除
        /// </summary>
        /// <param name="lines">所有要除噪音线的直线集合</param>
        /// <param name="line">基线</param>
        /// <param name="verticalLines">与除噪音线集合垂直的线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>是否移除</returns>
        private static bool removeNoiseLine(ref List<Line> lines, Line line, List<Line> verticalLines, int x)
        {
            var tempLines = lines.FindAll(l => l.A < line.A + x && l.A > line.A - x && l.A != line.A);

            if (tempLines.Count > 0)
            {
                foreach (var tempLine in tempLines)
                {
                    //判断两线是否同簇，不同簇的不予处理
                    if (IsOnSameCluster(tempLine, line))
                    {
                        var noiseLine = getSortLine(line, tempLine);
                        //如果此线有两个交点，则认为不是噪音线
                        var vLines = verticalLines.FindAll(
                                    l =>
                                    (l.A <= noiseLine.B_Max && l.A >= noiseLine.B_Min) &&
                                    (l.B_Min <= noiseLine.A && l.B_Max >= noiseLine.A));
                        if (vLines.Count <= 1)//可以允许有一个交点
                        {
                            lines.Remove(noiseLine);
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 判断是否是孤立线
        /// </summary>
        /// <param name="line">要判断的线</param>
        /// <param name="verticalLines">垂直线集合</param>
        /// <param name="minLength">最小长度</param>
        /// <returns>是否是孤立线</returns>
        private static bool isIsolateLine(Line line, List<Line> verticalLines, int minLength)
        {
            if (line.B_Max - line.B_Min > minLength)
            {
                return false;
            }

            var verticalL = verticalLines.FindAll(l => l.A > line.B_Min - 50 && l.A < line.B_Max + 50);
            if (verticalL.Count > 0)
            {
                foreach (var l in verticalL)
                {
                    //其它任何直线的点不落在基线的50像素范围内
                    if ((l.B_Min < line.A - 50 && l.B_Max >= line.A - 50) || (l.B_Min <= line.A + 50 && l.B_Max > line.A + 50))
                    {
                        return false;
                    }
                }
            }
            else
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 得到两条直线中较短的直线
        /// </summary>
        /// <param name="line1">第一条直线</param>
        /// <param name="line2">第二条直线</param>
        /// <returns>较短的直线</returns>
        private static Line getSortLine(Line line1, Line line2)
        {
            var l1 = line1.B_Max - line1.B_Min;
            var l2 = line2.B_Max - line2.B_Min;

            return l1 > l2 ? line2 : line1;
        }

        /// <summary>
        /// 去边界噪音线
        /// </summary>
        /// <param name="hLines">所有横线</param>
        /// <param name="vLines">所有竖线</param>
        /// <param name="w">图像宽度</param>
        /// <param name="h">图像高度</param>
        public static void DeleteEdgeLines(ref List<Line> hLines, ref List<Line> vLines, int w, int h)
        {
            int edgeAreaWl;
            int edgeAreaWr;
            int edgeAreaHt;
            int edgeAreaHb;

            if (w > h)
            {
                edgeAreaWl = w / 26;
                edgeAreaWr = w / 26 * 25;
                edgeAreaHt = h / 21;
                edgeAreaHb = h / 21 * 20;
            }
            else
            {
                edgeAreaWl = w / 21;
                edgeAreaWr = w / 21 * 20;
                edgeAreaHt = h / 26;
                edgeAreaHb = h / 26 * 25;
            }

            for (int i = 0; i < hLines.Count; i++)
            {
                var hLine = hLines[i];
                if (hLine.A < edgeAreaHt || hLine.A > edgeAreaHb)
                {
                    if (hLine.A < edgeAreaHt)
                    {
                        var crossVLines =
                        vLines.FindAll(
                            l =>
                            (l.A < ((hLine.B_Max > edgeAreaWr) ? edgeAreaWr : hLine.B_Max)) &&
                            (l.A > ((hLine.B_Min < edgeAreaWl) ? edgeAreaWl : hLine.B_Min)) && (l.B_Min <= hLine.A));

                        if (crossVLines.Count > 0)
                        {
                            continue;
                        }
                        hLines.Remove(hLine);
                        i--;
                    }
                    else if (hLine.A > edgeAreaHb)
                    {
                        var crossVLines =
                        vLines.FindAll(
                            l =>
                            (l.A < ((hLine.B_Max > edgeAreaWr) ? edgeAreaWr : hLine.B_Max)) &&
                            (l.A > ((hLine.B_Min < edgeAreaWl) ? edgeAreaWl : hLine.B_Min)) && (l.B_Max >= hLine.A));

                        if (crossVLines.Count > 0)
                        {
                            continue;
                        }
                        hLines.Remove(hLine);
                        i--;
                    }
                }
            }

            for (int i = 0; i < vLines.Count; i++)
            {
                var vLine = vLines[i];
                if (vLine.A < edgeAreaWl || vLine.A > edgeAreaWr)
                {
                    if (vLine.A < edgeAreaWl)
                    {
                        var crossHLines =
                        hLines.FindAll(
                            l =>
                            (l.A < ((vLine.B_Max > edgeAreaHb) ? edgeAreaHb : vLine.B_Max)) &&
                            (l.A > ((vLine.B_Min < edgeAreaHt) ? edgeAreaHt : vLine.B_Min)) && (l.B_Min <= vLine.A));

                        if (crossHLines.Count > 0)
                        {
                            continue;
                        }
                        vLines.Remove(vLine);
                        i--;
                    }
                    else if (vLine.A > edgeAreaWr)
                    {
                        var crossHLines =
                        hLines.FindAll(
                            l =>
                            (l.A < ((vLine.B_Max > edgeAreaHb) ? edgeAreaHb : vLine.B_Max)) &&
                            (l.A > ((vLine.B_Min < edgeAreaHt) ? edgeAreaHt : vLine.B_Min)) && (l.B_Max >= vLine.A));

                        if (crossHLines.Count > 0)
                        {
                            continue;
                        }
                        vLines.Remove(vLine);
                        i--;
                    }
                }
            }
        }

        #endregion

        #region 找线的交点

        /// <summary>
        /// 找线的所有交点并返回坐标
        /// </summary>
        /// <param name="hLines">所有横线</param>
        /// <param name="vLines">所有竖线</param>
        /// <returns>所有交点</returns>
        public static List<int[]> GetLinesNodes(List<Line> hLines, List<Line> vLines)
        {
            var nodeList = new List<int[]>();
            foreach (var hLine in hLines)
            {
                foreach (var vLine in vLines)
                {
                    var node = new int[2];
                    if (getNodeAtTwoLines(hLine, vLine, ref node))
                    {
                        nodeList.Add(node);
                    }
                }
            }
            return nodeList;
        }

        /// <summary>
        /// 判断两直线是否相交，并获取两直线的交点
        /// </summary>
        /// <param name="hLine">横线</param>
        /// <param name="vLine">竖线</param>
        /// <param name="node">两直线的交点坐标 </param>
        /// <returns>是否相交</returns>
        private static bool getNodeAtTwoLines(Line hLine, Line vLine, ref int[] node)
        {
            var flag = false;
            //注意临界值处理
            if ((hLine.B_Min <= vLine.A + 1 && vLine.A - 1 <= hLine.B_Max) && (vLine.B_Min <= hLine.A + 1 && hLine.A - 1 <= vLine.B_Max))
            {
                flag = true;
                node[0] = vLine.A;
                node[1] = hLine.A;
            }
            return flag;
        }

        #endregion

        #region 直线补全方法

        /// <summary>
        /// 直线补全方法
        /// </summary>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="w">影像宽</param>
        /// <param name="h">影像高</param>
        /// <param name="x">探测距离</param>
        public static void LinesComplete(ref List<Line> hLines, ref List<Line> vLines, int w, int h, int x)
        {
            //横线补全
            hLinesComplete(ref hLines, w, x);
            //竖线补全
            vLinesComplete(ref vLines, h, x);
        }

        /// <summary>
        /// 横线补全
        /// </summary>
        /// <param name="vHines">所有横线集合</param>
        /// <param name="w">影像宽</param>
        /// <param name="x">探测距离</param>
        private static void hLinesComplete(ref List<Line> vHines, int w, int x)
        {
            for (int i = 0; i < vHines.Count; i++)
            {
                //向右探测
                for (int j = 1; j <= x; j++)
                {
                    var curLine = vHines[i];
                    if (curLine.B_Max + j >= w)
                    {
                        break;
                    }
                    var findLine =
                        vHines.FirstOrDefault(
                        l => l.B_Min == curLine.B_Max + j && (l.A < curLine.A + 10 && l.A > curLine.A - 10));
                    if (findLine != null)
                    {
                        vHines[i].A = (findLine.B_Max - findLine.B_Min) - (curLine.B_Max - curLine.B_Min) > 0
                                       ? findLine.A
                                       : curLine.A;
                        vHines[i].B_Min = curLine.B_Min;
                        vHines[i].B_Max = findLine.B_Max;
                        vHines.Remove(findLine);
                        i--;
                        break;
                    }
                }
            }

            for (int i = 0; i < vHines.Count; i++)
            {
                //向左探测
                for (int j = 1; j <= x; j++)
                {
                    var curLine = vHines[i];
                    if (curLine.B_Min - j <= 0)
                    {
                        break;
                    }
                    var findLine =
                        vHines.FirstOrDefault(
                            l => l.B_Max == curLine.B_Min - j && (curLine.A + 10 < l.A && l.A > curLine.A - 10));
                    if (findLine != null)
                    {
                        vHines[i].A = (findLine.B_Max - findLine.B_Min) - (curLine.B_Max - curLine.B_Min) > 0
                                       ? findLine.A
                                       : curLine.A;
                        vHines[i].B_Min = findLine.B_Min;
                        vHines[i].B_Max = curLine.B_Max;
                        vHines.Remove(findLine);
                        i--;
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 竖线补全
        /// </summary>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="h">影像高 </param>
        /// <param name="x">探测距离</param>
        private static void vLinesComplete(ref List<Line> vLines, int h, int x)
        {
            for (int i = 0; i < vLines.Count; i++)
            {
                //向下探测
                for (int j = 1; j <= x; j++)
                {
                    var curLine = vLines[i];
                    if (curLine.B_Max + j >= h)
                    {
                        break;
                    }
                    var findLine =
                        vLines.FirstOrDefault(
                        l => l.B_Min == curLine.B_Max + j && (l.A < curLine.A + 10 && l.A > curLine.A - 10));
                    if (findLine != null)
                    {
                        vLines[i].A = (findLine.B_Max - findLine.B_Min) - (curLine.B_Max - curLine.B_Min) > 0
                                       ? findLine.A
                                       : curLine.A;
                        vLines[i].B_Min = curLine.B_Min;
                        vLines[i].B_Max = findLine.B_Max;
                        vLines.Remove(findLine);
                        i--;
                        break;
                    }
                }
            }

            for (int i = 0; i < vLines.Count; i++)
            {
                //向上探测
                for (int j = 1; j <= x; j++)
                {
                    var curLine = vLines[i];
                    if (curLine.B_Min - j <= 0)
                    {
                        break;
                    }
                    var findLine =
                        vLines.FirstOrDefault(
                            l => l.B_Max == curLine.B_Min - j && (curLine.A - 10 < l.A && l.A < curLine.A + 10));
                    if (findLine != null)
                    {
                        vLines[i].A = (findLine.B_Max - findLine.B_Min) - (curLine.B_Max - curLine.B_Min) > 0
                                       ? findLine.A
                                       : curLine.A;
                        vLines[i].B_Min = findLine.B_Min;
                        vLines[i].B_Max = curLine.B_Max;
                        vLines.Remove(findLine);
                        i--;
                        break;
                    }
                }
            }
        }

        #endregion

        #region 直线修正

        /// <summary>
        /// 直线修正补全方法-根据阈值探测相交线
        /// </summary>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="x">阈值 允许两直线错开的距离</param>
        /// <returns>直线补全后的直线集合</returns>
        public static void CorrectLines(ref List<Line> hLines, ref List<Line> vLines, int x)
        {
            //探测修正：两直线端点相互靠近，在阈值范围内相遇则说明两直线为一条直线，前提是在探测过程中不能与第三条线相交
            //然后按长的直线坐标进行修正

            //横线探测补全
            for (int i = 0; i < hLines.Count; i++)
            {
                //横线向左探测
                hLines[i].B_Min = leftCompleteHLine(hLines[i], vLines, x);

                //横线向右探测补全
                hLines[i].B_Max = rightCompleteHLine(hLines[i], vLines, x);
            }

            //竖线探测补全
            for (int i = 0; i < vLines.Count; i++)
            {
                //竖线向上探测
                vLines[i].B_Min = upCompleteVLine(vLines[i], hLines, x);

                //竖线向下探测
                vLines[i].B_Max = downCompleteVLine(vLines[i], hLines, x);
            }
        }

        /// <summary>
        /// 向左补全横线
        /// </summary>
        /// <param name="hLine">要补全的横线</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的横线B_Min值</returns>
        private static int leftCompleteHLine(Line hLine, List<Line> vLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var vLine in vLines)
                {
                    //向左延伸
                    if (vLine.A == hLine.B_Min - i)
                    {
                        //两线相交的条件
                        if ((vLine.B_Min < hLine.A && vLine.B_Max >= hLine.A - x) || (vLine.B_Min <= hLine.A + x && vLine.B_Max > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                    //向右收缩
                    if (vLine.A == hLine.B_Min + i)
                    {
                        //两线相交的条件
                        if ((vLine.B_Min < hLine.A && vLine.B_Max >= hLine.A - x) || (vLine.B_Min <= hLine.A + x && vLine.B_Max > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                }
            }

            return hLine.B_Min;
        }

        /// <summary>
        /// 向右补全横线
        /// </summary>
        /// <param name="hLine">要补全的横线</param>
        /// <param name="vLines">所有竖线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的横线B_Max值</returns>
        private static int rightCompleteHLine(Line hLine, List<Line> vLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var vLine in vLines)
                {
                    //向右延伸
                    if (vLine.A == hLine.B_Max + i)
                    {
                        //两线相交的条件
                        if ((vLine.B_Min < hLine.A && vLine.B_Max >= hLine.A - x) || (vLine.B_Min <= hLine.A + x && vLine.B_Max > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                    //向左收缩
                    if (vLine.A == hLine.B_Max - i)
                    {
                        //两线相交的条件
                        if ((vLine.B_Min < hLine.A && vLine.B_Max >= hLine.A - x) || (vLine.B_Min <= hLine.A + x && vLine.B_Max > hLine.A))
                        {
                            return vLine.A;
                        }
                    }
                }
            }

            return hLine.B_Max;
        }

        /// <summary>
        /// 向上补全竖线
        /// </summary>
        /// <param name="vLine">要补全的竖线</param>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的竖线B_Min值</returns>
        private static int upCompleteVLine(Line vLine, List<Line> hLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var hLine in hLines)
                {
                    //向上延伸
                    if (hLine.A == vLine.B_Min - i)
                    {
                        //两线相交的条件
                        if ((hLine.B_Min < vLine.A && hLine.B_Max >= vLine.A - x) || (hLine.B_Min <= vLine.A + x && hLine.B_Max > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                    //向下收缩
                    if (hLine.A == vLine.B_Min + i)
                    {
                        //两线相交的条件
                        if ((hLine.B_Min < vLine.A && hLine.B_Max >= vLine.A - x) || (hLine.B_Min <= vLine.A + x && hLine.B_Max > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                }
            }

            return vLine.B_Min;
        }

        /// <summary>
        /// 向下补全竖线
        /// </summary>
        /// <param name="vLine">要补全的竖线</param>
        /// <param name="hLines">所有横线集合</param>
        /// <param name="x">阈值</param>
        /// <returns>补全后的竖线B_Max值</returns>
        private static int downCompleteVLine(Line vLine, List<Line> hLines, int x)
        {
            for (int i = 0; i <= x; i++)
            {
                foreach (var hLine in hLines)
                {
                    //向下延伸
                    if (hLine.A == vLine.B_Max + i)
                    {
                        //两线相交的条件
                        if ((hLine.B_Min < vLine.A && hLine.B_Max >= vLine.A - x) || (hLine.B_Min <= vLine.A + x && hLine.B_Max > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                    //向上收缩
                    if (hLine.A == vLine.B_Max - i)
                    {
                        //两线相交的条件
                        if ((hLine.B_Min < vLine.A && hLine.B_Max >= vLine.A - x) || (hLine.B_Min <= vLine.A + x && hLine.B_Max > vLine.A))
                        {
                            return hLine.A;
                        }
                    }
                }
            }

            return vLine.B_Max;
        }

        #endregion

        #region 分析直线找内容区

        public static Rectangle AnalysisLinesFindContentArea(List<Line> hLines, List<Line> vLines, int w, int h)
        {
            var rec = new Rectangle();

            var ts = hLines.Count / 2;
            var vs = vLines.Count / 2;
            int x = 0, y = 0;
            for (int i = 1; i < w; i++)
            {
                var findLines = hLines.FindAll(l => l.B_Min == i);
                if (findLines.Count >= ts)
                {
                    rec.X = i;
                    break;
                }
            }

            for (int i = 1; i < w; i++)
            {
                var findLines = hLines.FindAll(l => l.B_Max == w - i);
                if (findLines.Count >= ts)
                {
                    x = w - i;
                    break;
                }
            }

            for (int j = 1; j < h; j++)
            {
                var findLines = vLines.FindAll(l => l.B_Min == j);
                if (findLines.Count >= vs)
                {
                    rec.Y = j;
                    break;
                }
            }

            for (int j = 1; j < h; j++)
            {
                var findLines = vLines.FindAll(l => l.B_Max == h - j);
                if (findLines.Count >= vs)
                {
                    y = h - j;
                    break;
                }
            }

            rec.Width = x - rec.X;
            rec.Height = y - rec.Y;

            return rec;
        }

        #endregion

        #endregion

        #region 影像分析

        /// <summary>
        /// 根据线条分析影像类型（列表类与发票形式类）
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <param name="w">影像宽</param>
        /// <param name="h">影像高</param>
        /// /// <param name="rectangle">药品区域范围</param>
        /// <returns>影像类型</returns>
        public static ImageType AnalysicImageClass(List<Line> hLines, List<Line> vLines, int w, int h, ref Rectangle rectangle)
        {
            //*******************************************************
            //*目前所有影像定义为四类：
            //* 1、上海发票类影像--左右结构，左边有矩形框，右边有一条或两条横线
            //* 2、表格结构影像--明显的列表结构，所有字段在一个个表格格子内
            //* 3、带框结构影像--整个影像的重心是框架，内容在其中最大的矩形框内
            //* 4、普通结构影像--整个影像的行特征明显，有横线或没横线，线条之间没有什么规律
            //*******************************************************

            //影像类型
            var type = ImageType.Unknown;

            //分析是否是上海发票类
            var rLine = new Line();
            if (isShanghaiBill(hLines, vLines,w, ref rLine))
            {
                type = ImageType.Bill;
                var point = new[] { rLine.A, rLine.B_Min };
                var hLine = hLines.FirstOrDefault(l => l.A < point[1] + 60 && l.B_Max > point[0] + 100 && l.B_Max != point[0]);
                var hLine1 = hLines.FirstOrDefault(l => l.A == rLine.B_Min && l.B_Max != point[0]);
                var hLine2 = hLines.FindAll(l => l.B_Min > rLine.A);
                if (hLine != null)
                {
                    rectangle.X = rLine.A + 10;
                    rectangle.Y = hLine.A + 10;
                    rectangle.Width = hLine.B_Max - rLine.A - 10;
                    rectangle.Height = rLine.B_Max - hLine.A - 20;
                }
                else if(hLine1 != null)
                {
                    rectangle.X = rLine.A + 10;
                    rectangle.Y = hLine1.A + 10;
                    rectangle.Width = hLine1.B_Max - rLine.A - 10;
                    rectangle.Height = rLine.B_Max - hLine1.A - 20;
                }
                else if(hLine2.Count == 0)
                {
                    rectangle.X = rLine.A + 10;
                    rectangle.Y = 0;
                    rectangle.Width = w - rLine.A - 10;
                    rectangle.Height = rLine.B_Max - 20;
                }
                //var vLine = leftDetectFindVLine(hLine, vLines);
                //if (vLine.A != 0)
                //{
                //    type = ImageType.Bill;
                //    rectangle.X = vLine.A + 10;
                //    rectangle.Y = hLine.A + 10;
                //    rectangle.Width = hLine.B_Max - vLine.A - 10;
                //    rectangle.Height = vLine.B_Max - hLine.A - 20;
                //}
                //else
                //{
                //    //右边没有横线的情况，拿出来当列表处理，待开发
                //    type = ImageType.TableDetail;
                //    rectangle = new Rectangle(0, 0, w, h);
                //}
            }
            else if (isTableDetailImage(hLines, vLines))
            {
                type = ImageType.TableDetail;
                var x = vLines[0].A;
                x = vLines.Select(vLine => vLine.A).Concat(new[] { x }).Min();
                var y = hLines[0].A;
                y = hLines.Select(line => line.A).Concat(new[] { y }).Min();
                var x1 = vLines[0].A;
                x1 = vLines.Select(vLine => vLine.A).Concat(new[] { x1 }).Max();
                var y1 = hLines[0].A;
                y1 = hLines.Select(line => line.A).Concat(new[] { y1 }).Max();
                rectangle = new Rectangle(x, y, x1 - x, y1 - y);
            }
            else if (isFrameDetailImage(hLines, vLines))
            {
                type = ImageType.FrameDetail;
                var nodes = GetLinesNodes(hLines, vLines);
                rectangle = FindFrameImageContentArea(nodes);
                rectangle.X = rectangle.X + 5;
                rectangle.Y = rectangle.Y + 5;
                rectangle.Width = rectangle.Width - 5;
                rectangle.Height = rectangle.Height - 5;
            }
            else if (isNormalDetailImage(hLines, vLines))
            {
                type = ImageType.NormalDetail;
                rectangle = findNormalImageContentArea(hLines, vLines, w, h);
            }

            return type;
        }

        #region 上海影像判别方法

        /// <summary>
        /// 判断是否属于上海发票类
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <param name="dLine">找到的横线基线</param>
        /// <returns>是否属于上海发票类</returns>
        private static bool isShanghaiBill(List<Line> hLines, List<Line> vLines,int w, ref Line dLine)
        {
            //影像特征：首先找到一根横线，它的下面必有最少5根横线，它的右边（上下浮动不超10像素）必有一根横线，与它相交有两竖线
            if (hLines.Count < 6 || vLines.Count < 2 || hLines.Count > 10 || vLines.Count > 5)
            {
                return false;
            }

            return shanghaiBillAnalysis(hLines, vLines,w, ref dLine);

            //foreach (var hLine in hLines)
            //{
            //    //将基线长度向左向右各延伸60像素，可接受的误差
            //    var min = hLine.B_Min - 60;
            //    var max = hLine.B_Max + 60;

            //    var findLines = new List<Line>();
            //    foreach (var line in hLines)
            //    {
            //        if (line.B_Min >= min && line.B_Max <= max)
            //        {
            //            findLines.Add(line);
            //        }
            //    }

            //    if (findLines.Count >= 4 && findLines.Count <= 10)
            //    {
            //        if (isFindHLine(hLines, hLine, ref dLine))
            //        {
            //            return true;
            //        }
            //    }
            //}

            //return false;
        }

        /// <summary>
        /// 上海发票影像分析方法
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="vLines">竖线集合</param>
        /// <param name="w">影像宽度</param>
        /// <param name="vLine">要找的竖线</param>
        /// <returns>是否是上海发票</returns>
        private static bool shanghaiBillAnalysis(List<Line> hLines, List<Line> vLines,int w, ref Line vLine)
        {
            foreach (var hLine in hLines)
            {
                //将基线长度向左向右各延伸60像素，可接受的误差
                var min = hLine.B_Min - 10;
                var max = hLine.B_Max + 10;

                if(max - min > w/2)
                {
                    continue;
                }

                var findLines = new List<Line>();
                foreach (var line in hLines)
                {
                    if (line.B_Min >= min && line.B_Max <= max)
                    {
                        findLines.Add(line);
                    }
                }

                if (findLines.Count >= 4 && findLines.Count <= 10)
                {
                    foreach (var findLine in findLines)
                    {
                        foreach (var line in vLines)
                        {
                            if (line.A == findLine.B_Max)
                            {
                                vLine = line;
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 向右探测是否找到横线，探测距离是基线长度的一半，探测浮动是20像素
        /// </summary>
        /// <param name="hLines">横线集合</param>
        /// <param name="line">基线</param>
        /// <param name="dLine">找到的横线基线</param>
        /// <returns>是否找到</returns>
        private static bool isFindHLine(List<Line> hLines, Line line, ref Line dLine)
        {
            var x = (line.B_Max - line.B_Min) / 2;
            for (int i = 0; i < x; i++)
            {
                var newMax = line.B_Max + i;
                foreach (var hLine in hLines)
                {
                    if (hLine.B_Min == newMax)
                    {
                        if (hLine.A < line.A + 60 && hLine.A > line.A - 60)
                        {
                            dLine = hLine;
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// 向左探测找竖线，探测距离是基线长度的一半
        /// </summary>
        /// <param name="line">基线</param>
        /// <param name="vLines">竖线集合</param>
        /// <returns>找到的竖线</returns>
        private static Line leftDetectFindVLine(Line line, List<Line> vLines)
        {
            var x = (line.B_Max - line.B_Min) / 2;//设置探测阈值
            var newLine = new Line();
            for (int i = 1; i <= x; i++)
            {
                var newMin = line.B_Min - i;
                foreach (var vLine in vLines)
                {
                    if (vLine.A == newMin && (vLine.B_Min >= line.A - 100 && vLine.B_Min <= line.A + 100))//待测试
                    {
                        newLine = vLine;
                    }
                }
            }
            return newLine;
        }

        #endregion

        #region 表格结构影像判别方法

        private static bool isTableDetailImage(List<Line> hLines, List<Line> vLines)
        {
            if (hLines.Count < 10 || vLines.Count <= 2)
            {
                return false;
            }

            return true;
        }

        #endregion

        #region 带框影像判别方法

        private static bool isFrameDetailImage(List<Line> hLines, List<Line> vLines)
        {
            var nodes = GetLinesNodes(hLines, vLines);
            if (nodes.Count < hLines.Count * vLines.Count / 2)
            {
                return false;
            }
            if ((hLines.Count >= 4 && hLines.Count <= 7) && (vLines.Count >= 2 && vLines.Count <= 7))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 根据点分析带框影像药品区（所有点构成）
        /// </summary>
        /// <param name="nodes">直线相交节点</param>
        /// <returns>最大矩形框（即药品区域）</returns>
        public static Rectangle FindFrameImageContentArea(List<int[]> nodes)
        {
            var rectangle = new Rectangle();

            foreach (var node in nodes)
            {
                var x = node[0];
                var y = node[1];

                var rightNodes = nodes.Where(n => n[1] == y && n[0] > x).OrderBy(d => d[0]);
                if (rightNodes.Any())
                {
                    foreach (var rightNode in rightNodes)
                    {
                        var lowerNodes = nodes.Where(n => n[1] > y && n[0] == x).OrderBy(d => d[1]);
                        if (lowerNodes.Any())
                        {
                            foreach (var lowerNode in lowerNodes)
                            {
                                if (nodes.Exists(n => n[0] == rightNode[0] && n[1] == lowerNode[1]))
                                {
                                    if ((rightNode[0] - x) * (lowerNode[1] - y) > rectangle.Width * rectangle.Height)
                                    {
                                        rectangle.X = x;
                                        rectangle.Y = y;
                                        rectangle.Width = rightNode[0] - x;
                                        rectangle.Height = lowerNode[1] - y;
                                    }
                                    goto Found;
                                }
                            }
                        }
                    }
                }
            Found:
                ;

            }

            return rectangle;
        }

        #endregion

        #region 普通结构影像判别方法

        private static bool isNormalDetailImage(List<Line> hLines, List<Line> vLines)
        {
            var flag = false;

            if (hLines.Count > 0 && vLines.Count <= 2)
            {
                flag = true;
            }
            else if (hLines.Count <= 2 && vLines.Count > 0)
            {
                flag = true;
            }

            return flag;
        }

        /// <summary>
        /// 根据点分析带框影像药品区（所有点构成）
        /// </summary>
        /// <returns>最大矩形框（即药品区域）</returns>
        private static Rectangle findNormalImageContentArea(List<Line> hLines, List<Line> vLines, int w, int h)
        {
            var rectangle = new Rectangle(0, 0, w, h);

            var hLength = 0;
            var vLength = 0;
            foreach (var hLine in hLines)
            {
                hLength += (hLine.B_Max - hLine.B_Min);
            }
            foreach (var vLine in vLines)
            {
                vLength += (vLine.B_Max - vLine.B_Min);
            }

            if (hLength > vLength)
            {
                var max = hLines[0].B_Max - hLines[0].B_Min;
                var maxHline = hLines[0];
                foreach (var hLine in hLines)
                {
                    if (max < (hLine.B_Max - hLine.B_Min))
                    {
                        max = hLine.B_Max - hLine.B_Min;
                        maxHline = hLine;
                    }
                }

                //找第一根横线
                var topLine = hLines[0];
                foreach (var hLine in hLines)
                {
                    if (topLine.A > hLine.A)
                    {
                        topLine = hLine;
                    }
                }

                if (max > w / 3 * 2)
                {
                    rectangle = new Rectangle(maxHline.B_Min, topLine.A, max, h - topLine.A);
                }
            }
            else if (vLength > hLength)
            {
                var max = vLines[0].B_Max - vLines[0].B_Min;
                var maxHline = vLines[0];
                foreach (var vLine in vLines)
                {
                    if (max < (vLine.B_Max - vLine.B_Min))
                    {
                        max = vLine.B_Max - vLine.B_Min;
                        maxHline = vLine;
                    }
                }

                if (max > w / 3 * 2)
                {
                    rectangle = new Rectangle(0, maxHline.B_Min, w, max);
                }
            }

            return rectangle;
        }

        #endregion

        #region 药品区投影分析找行列 方案一：逐行投影分析

        /// <summary>
        /// 分析投影，找到行区域
        /// </summary>
        /// <param name="gs">投影二值矩阵</param>
        /// <param name="w">投影宽</param>
        /// <param name="h">投影高</param>
        /// <param name="x">阈值 允许两行间相邻最大像素值</param>
        /// <param name="hArea">阈值 超过此值即认为是一行</param>
        /// <returns>意义行矩形</returns>
        public static List<Rectangle> GetRowArea(byte[,] gs, int w, int h, int x, int hArea)
        {
            var rs = new List<Rectangle>();
            var n = 0;
            var b = 0;
            for (int j = 0; j < h; j++)
            {
                if (gs[x, j] == 1)
                {
                    n++;
                    b = 0;
                }
                else
                {
                    if (n >= hArea)//超过hArea像素则认为是一行
                    {
                        var y1 = j - n - 10 < 0 ? 0 : j - n - 10;//边界补充修正，避免字体刚好压线
                        var y2 = j + 10 > h - 1 ? h - 1 : j + 10;//边界补充修正，避免字体刚好压线
                        var r = new Rectangle(0, y1, w, y2 - y1);
                        rs.Add(r);
                    }

                    n = 0;

                    b++;

                    //去除药品内容区底部杂乱信息干扰，超出50像素空白则认为后面信息为干扰信息
                    if (b > 50 && rs.Count > 1)
                    {
                        break;
                    }
                }
            }

            return rs;
        }

        /// <summary>
        /// 分析列，将每行进行投影分成列-断点法
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangles">行矩形</param>
        /// <returns>分成列的行矩形列表</returns>
        public static List<List<Rectangle>> AnalysisColumn1(byte[,] im, List<Rectangle> rectangles)
        {
            var breakPointsList = new List<List<int>>();
            for (int i = 0; i < rectangles.Count; i++)
            {
                var gs = rectangleProjection(im, rectangles[i]);
                breakPointsList.Add(rectangleProjectionAnalysis1(gs));
            }

            //根据断点将行矩形分列
            var listRectangles = new List<List<Rectangle>>();
            var listRectangle = new List<Rectangle>();
            for (int i = 0; i < rectangles.Count; i++)
            {
                var cursor = 0;
                foreach (var breakPoint in breakPointsList[i])
                {
                    var x = rectangles[i].X + cursor;
                    var y = rectangles[i].Y;
                    var w = breakPoint - cursor;
                    var h = rectangles[i].Height;

                    var r = new Rectangle(x, y, w, h);
                    listRectangle.Add(r);
                    cursor = breakPoint;
                }
                listRectangles.Add(listRectangle);
                listRectangle = new List<Rectangle>();
            }

            return listRectangles;
        }

        /// <summary>
        /// 分析列，将每行进行投影分成列-分段法
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangles">行矩形</param>
        /// <returns>分成列的行矩形列表</returns>
        public static List<List<Rectangle>> AnalysisColumn2(byte[,] im, List<Rectangle> rectangles)
        {
            var segmentList = new List<List<int[]>>();//分段列表
            for (int i = 0; i < rectangles.Count; i++)
            {
                var gs = rectangleProjection(im, rectangles[i]);
                var intobjs = rectangleProjectionAnalysis2(gs);
                if (intobjs.Count <= 6)
                {
                    segmentList.Add(intobjs);
                }
                else
                {
                    rectangles.Remove(rectangles[i]);
                    i--;
                }
            }

            //根据断点将行矩形分列
            var listRectangles = new List<List<Rectangle>>();
            var listRectangle = new List<Rectangle>();
            for (int i = 0; i < rectangles.Count; i++)
            {
                foreach (var segment in segmentList[i])
                {
                    var x = segment[0] - 15 < 0 ? 0 : segment[0] - 15;
                    var y = rectangles[i].Y;
                    var w = segment[1] + 30 + x > rectangles[i].Width - 1 ? rectangles[i].Width - x - 1 : segment[1] + 30;
                    var h = rectangles[i].Height;

                    var r = new Rectangle(x, y, w, h);
                    listRectangle.Add(r);
                }
                listRectangles.Add(listRectangle);
                listRectangle = new List<Rectangle>();
            }

            return listRectangles;
        }

        /// <summary>
        /// 分析列，将每行进行投影分成列-分段法，只保留第一列
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangles">行矩形</param>
        /// <returns>分成列的行矩形列表</returns>
        public static List<List<Rectangle>> AnalysisColumn3(byte[,] im, List<Rectangle> rectangles)
        {
            var segmentList = new List<List<int[]>>();//分段列表
            for (int i = 0; i < rectangles.Count; i++)
            {
                var gs = rectangleProjection(im, rectangles[i]);
                var intobjs = rectangleProjectionAnalysis3(gs);
                if (intobjs.Count <= 6)
                {
                    segmentList.Add(intobjs);
                }
                else
                {
                    rectangles.Remove(rectangles[i]);
                    i--;
                }
            }

            //多行处理
            moreLinesProcessing(ref segmentList, ref rectangles);

            //只留药品名称区(只留第一列)
            //for (int i = 0; i < segmentList.Count; i++)
            //{
            //    var ints = segmentList[i][0];
            //    segmentList[i].RemoveAll(l => l != null);
            //    segmentList[i].Add(ints);
            //}

            //根据断点将行矩形分列
            var listRectangles = new List<List<Rectangle>>();
            var listRectangle = new List<Rectangle>();
            for (int i = 0; i < rectangles.Count; i++)
            {
                foreach (var segment in segmentList[i])
                {
                    var x = segment[0] - 8 < 0 ? 0 : segment[0] - 8;
                    var y = rectangles[i].Y;
                    var w = segment[1] + 16 + x > rectangles[i].Width - 1 ? rectangles[i].Width - x - 1 : segment[1] + 16;
                    var h = rectangles[i].Height;

                    var r = new Rectangle(x, y, w, h);
                    listRectangle.Add(r);
                }
                listRectangles.Add(listRectangle);
                listRectangle = new List<Rectangle>();
            }

            return listRectangles;
        }

        /// <summary>
        /// 对行矩形进行横向投影
        /// </summary>
        /// <param name="im">影像二值矩阵</param>
        /// <param name="rectangle">行矩形</param>
        /// <returns>投影后的二值矩阵</returns>
        private static byte[,] rectangleProjection(byte[,] im, Rectangle rectangle)
        {
            var rIm = new byte[rectangle.Width, rectangle.Height];

            for (int j = rectangle.Y; j < rectangle.Y + rectangle.Height; j++)
            {
                for (int i = 0; i < rectangle.Width; i++)
                {
                    rIm[i, j - rectangle.Y] = im[i, j];
                }
            }

            return Projection.VerticalProjection(rIm);
        }

        /// <summary>
        /// 矩形分列投影分析--断点法
        /// </summary>
        /// <param name="gs">矩阵块投影二值矩阵</param>
        /// <returns>分列断点数组</returns>
        private static List<int> rectangleProjectionAnalysis1(byte[,] gs)
        {
            var w = gs.GetLength(0);
            var h = gs.GetLength(1);

            var ws = new List<int>();
            var n = 0;
            var b = 0;
            for (int i = 0; i < w; i++)
            {
                if (gs[i, h - 1] == 1)
                {
                    n++;
                    if (b > 0 && b <= 27)
                    {
                        n = n + b;
                        b = 0;
                    }
                }
                else
                {
                    if (n > 0)
                    {
                        b++;
                    }

                    if (b > 27)//空白区域长度超过27像素，则认为是另起一列，并记录当前断点
                    {
                        if (i != w - 1)//排除最后一个断点
                        {
                            ws.Add(i);
                            n = 0;
                            b = 0;
                        }
                    }
                }

                if (i == w - 1 && n <= 3 && ws.Count > 1)
                {
                    ws.Remove(ws[ws.Count - 1]);
                }
            }

            return ws;
        }

        /// <summary>
        /// 矩形分列投影分析--分段法
        /// </summary>
        /// <param name="gs">矩阵块投影二值矩阵</param>
        /// <returns>分段数组列表[起始点，长度]</returns>
        private static List<int[]> rectangleProjectionAnalysis2(byte[,] gs)
        {
            var w = gs.GetLength(0);
            var h = gs.GetLength(1);

            var ws = new List<int[]>();
            var n = 0;
            var b = 0;
            for (int i = 0; i < w; i++)
            {
                if (gs[i, h - 1] == 1)
                {
                    n++;
                    if (b > 0 && b <= 27)
                    {
                        n = n + b;
                        b = 0;
                    }
                }
                else
                {
                    if (n > 0)
                    {
                        b++;
                    }
                }

                if (b > 27)//空白区域长度超过27像素，则认为是另起一列，并记录当前断点
                {
                    if (n > 16)
                    {
                        var intobj = new int[2];
                        intobj[0] = i - b - n;
                        intobj[1] = n;
                        ws.Add(intobj);
                    }

                    n = 0;
                    b = 0;
                }
                else if (i == w - 1 && n > 64)//边界处理
                {
                    var intobj = new int[2];
                    intobj[0] = i - n;
                    intobj[1] = n;
                    ws.Add(intobj);
                }
            }

            //所有内容在一个矩形内的情况
            if (ws.Count == 0)
            {
                var intobj = new int[2];
                intobj[0] = 0;
                intobj[1] = w;
                ws.Add(intobj);
            }

            return ws;
        }

        /// <summary>
        /// 矩形分列投影分析--逐行分析法
        /// </summary>
        /// <param name="gs">矩阵块投影二值矩阵</param>
        /// <returns>分段数组列表[起始点，长度]</returns>
        private static List<int[]> rectangleProjectionAnalysis3(byte[,] gs)
        {
            var w = gs.GetLength(0);
            var h = gs.GetLength(1);

            var ws = new List<int[]>();
            var n = 0;
            var b = 0;
            for (int i = 0; i < w; i++)
            {
                if (gs[i, h - 1] == 1)
                {
                    n++;
                    if (b > 0 && b <= 27)
                    {
                        n = n + b;
                        b = 0;
                    }
                }
                else
                {
                    if (n > 0)
                    {
                        b++;
                    }
                }

                if (b > 27)//空白区域长度超过27像素，则认为是另起一列，并记录当前断点
                {
                    if (n > 16)
                    {
                        //分段数组[起始点，长度]
                        var intobj = new int[2];
                        intobj[0] = i - b - n;
                        intobj[1] = n;
                        ws.Add(intobj);
                    }

                    n = 0;
                    b = 0;
                }
                else if (i == w - 1 && n > 64)//边界处理
                {
                    var intobj = new int[2];
                    intobj[0] = i - n;
                    intobj[1] = n;
                    ws.Add(intobj);
                }
            }

            //所有内容在一个矩形内的情况
            if (ws.Count == 0)
            {
                var intobj = new int[2];
                intobj[0] = 0;
                intobj[1] = w;
                ws.Add(intobj);
            }

            return ws;
        }

        /// <summary>
        /// 药品名占多行处理方法
        /// </summary>
        /// <param name="listIntses">分段数组列表</param>
        /// <param name="rectangles">矩形行列表</param>
        private static void moreLinesProcessing(ref List<List<int[]>> listIntses, ref List<Rectangle> rectangles)
        {
            for (int i = 1; i < listIntses.Count; i++)
            {
                var count1 = listIntses[i - 1].Count();
                var count2 = listIntses[i].Count();

                if (count1 >= 3 && count2 <= 2)
                {
                    if (count2 == 1 && listIntses[i][0][1] > rectangles[i].Width / 2)
                    {
                        continue;
                    }
                    var maxLength = listIntses[i - 1][0][1] > listIntses[i][0][1] ? listIntses[i - 1][0][1] : listIntses[i][0][1];
                    listIntses[i - 1][0][1] = maxLength;
                    listIntses.Remove(listIntses[i]);

                    //同步处理矩形，合并行矩形，重置高度
                    var newHeight = rectangles[i - 1].Height + rectangles[i].Height;
                    rectangles[i - 1] = new Rectangle(rectangles[i - 1].X, rectangles[i - 1].Y, rectangles[i - 1].Width, newHeight);
                    rectangles.Remove(rectangles[i]);
                }
            }
        }

        #endregion

        /// <summary>
        /// 判断两直线是否属于同一簇
        /// </summary>
        /// <param name="line1">直线1</param>
        /// <param name="line2">直线2</param>
        /// <returns>是否同簇</returns>
        public static bool IsOnSameCluster(Line line1, Line line2)
        {
            var flag = false;

            var lengthLine1 = line1.B_Max - line1.B_Min;
            var lengthLine2 = line2.B_Max - line2.B_Min;
            var rangeMax = line1.B_Max > line2.B_Max ? line1.B_Max : line2.B_Max;
            var rangeMin = line1.B_Min < line2.B_Min ? line1.B_Min : line2.B_Min;
            var rangeLength = rangeMax - rangeMin;//两条直线的范围长度

            //判断两直线是否同列条件：两直线的范围长度必小于两直线的总长度
            if (rangeLength < lengthLine1 + lengthLine2)
            {
                flag = true;
            }

            return flag;
        }

        #endregion
    }
}
